package com.gan.simple_init.aspect;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import com.gan.simple_init.annotation.OperLog;
import com.gan.simple_init.common.enums.BusinessStatus;
import com.gan.simple_init.mapper.SysOperationLogMapper;
import com.gan.simple_init.model.sys.entity.SysOperationLog;
import com.gan.simple_init.model.user.entity.LoginUser;
import com.gan.simple_init.util.IpUtils;
import com.gan.simple_init.util.UserCacheUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Map;

/**
 * 系统操作日志记录
 *
 * @author gbw
 **/
@Aspect
@Component
@Slf4j
public class OperLogAspect {

    @Resource
    UserCacheUtil userCacheUtil;

    @Resource
    SysOperationLogMapper sysOperationLogMapper;


    /**
     * 排除敏感字段
     */
    private static final String[] SENSITIVE_INFO = {"password", "oldPassword", "newPassword", "confirmPassword"};

    /**
     * 计算任务的执行时间
     */
    private static final ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<>();

    /**
     * 执行前拦截
     */
    @Before(value = "@annotation(operLog)")
    public void doBefore(JoinPoint point, OperLog operLog) {
        TIME_THREADLOCAL.set(System.currentTimeMillis());
    }


    /**
     * 正常执行后拦截
     */
    @AfterReturning(pointcut = "@annotation(operLog)", returning = "jsonResult")
    public void doAfter(JoinPoint point, OperLog operLog, Object jsonResult) {
        handleLog(point, operLog, null, jsonResult);
    }

    /**
     * 报错执行后拦截
     */
    @AfterThrowing(value = "@annotation(operLog)", throwing = "e")
    public void doThrow(JoinPoint point, OperLog operLog, Exception e) {
        handleLog(point, operLog, e, null);
    }

    protected void handleLog(JoinPoint point, OperLog operLog, Exception e, Object jsonResult) {
        try {
            SysOperationLog sysOperationLog = new SysOperationLog();
            // 设置开始执行时间
            Date beginTime = new Date();
            beginTime.setTime(TIME_THREADLOCAL.get());
            sysOperationLog.setOperTime(beginTime);


            // 设置当前用户相关信息
            LoginUser loginUser = UserCacheUtil.getUserCache();
            sysOperationLog.setOperName(loginUser.getUserName());
            sysOperationLog.setOperRole(userCacheUtil.getUserRole());

            // 先判断调用成功还是失败
            if (e != null) {
                sysOperationLog.setStatus(BusinessStatus.FAIL.getValue());
                sysOperationLog.setErrorMsg(e.getMessage());
            } else {
                sysOperationLog.setStatus(BusinessStatus.SUCCESS.getValue());
            }


            // 设置自带参数
            sysOperationLog.setTitle(operLog.title());
            sysOperationLog.setBusinessType(operLog.businessType().getValue());
            // 设置接口名称
            String className = point.getTarget().getClass().getName();
            String methodName = point.getSignature().getName();
            sysOperationLog.setMethod(className + "." + methodName + "()");

            // 设置请求方式
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            sysOperationLog.setRequestMethod(request.getMethod());
            // 设置请求参数
            Map<String, String[]> parameterMap = request.getParameterMap();
            String jsonStr = JSONObject.toJSONString(parameterMap,excludePropertyPreFilter(operLog.excludeParamNames()));
            sysOperationLog.setOperParam(jsonStr);
            // 获取请求ip
            sysOperationLog.setOperIp(IpUtils.getIpAddress(request));
            // 设置请求地址
            sysOperationLog.setOperUrl(request.getRequestURI());
            // 设置响应结果
            if (jsonResult != null){
                sysOperationLog.setJsonResult(jsonResult.toString());
            }

            // 设置消耗时间
            sysOperationLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());

            // 持久化
            sysOperationLogMapper.insert(sysOperationLog);
        } catch (Exception exp) {
            log.error("操作日志记录失败:{}",exp.getMessage());
            throw exp;
        } finally {
            TIME_THREADLOCAL.remove();
        }


    }

    /**
     * 忽略敏感属性
     */
    public PropertyPreFilters.MySimplePropertyPreFilter excludePropertyPreFilter(String[] excludeParamNames)
    {
        return new PropertyPreFilters().addFilter().addExcludes(ArrayUtils.addAll(SENSITIVE_INFO, excludeParamNames));
    }

}
















